package sg.com.techstudio.ecc.dao;

import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import sg.com.techstudio.ecc.filter.RegisterFilter;
import sg.com.techstudio.ecc.obj.LearningJourneyObj;
import sg.com.techstudio.ecc.obj.ParticipantObj;
import sg.com.techstudio.ecc.obj.RegisterCountObj;
import sg.com.techstudio.ecc.obj.RegisterObj;
import sg.com.techstudio.ecc.obj.SessionObj;

@Repository("registerDao")
@Transactional
public class RegisterDao {
	private static final Log log = LogFactory.getLog(RegisterDao.class);
	
//	private static Calendar EARLYBIRD_END_DATE;
	private static final double PRICE_FULL = 150;
	private static final int RESERVE_SEAT_IN_MINUTES = 19;
//	private static final double DISCOUNT = 0.2;
//	private static final String EARLYBIRD_PROMOCODE = "Early Bird Discount";
	
	@Autowired
	private SessionFactory sessionFactory;
	
	@Autowired
	private EmailDao emailDao;
	
	public boolean isNricExist(String nric, int regType) {
		log.debug("isNricExist() entry");
		Session ses = sessionFactory.getCurrentSession();
		Criteria c = ses.createCriteria(RegisterObj.class);
		c.add(Restrictions.eq("nric", nric));
		c.add(Restrictions.eq("regType", regType));
		c.add(Restrictions.or(Restrictions.eq("status", 1), Restrictions.ge("createdDate", getCutOffDate())));
		c.setProjection(Projections.rowCount());
		long res = (Long) c.uniqueResult();
		if(res>0)
			return true;
		if(regType>0) // only main registration has participant
			return false;
		c = ses.createCriteria(ParticipantObj.class);
		c.add(Restrictions.eq("nric", nric));
		c.add(Restrictions.or(Restrictions.eq("status", 1), Restrictions.ge("createdDate", getCutOffDate())));
		c.setProjection(Projections.rowCount());
		return c.uniqueResult()!=null && (Long) c.uniqueResult()>0;
	}
	
	public RegisterObj computeAmount(RegisterObj r) {
		log.debug("computeAmount() entry");
		if(r==null)
			return r;
		r.setAmount(PRICE_FULL * (1+r.countParticipants()));
		return r;
	}
	
	public RegisterObj computeAmount(RegisterObj r, List<LearningJourneyObj> learningJourneyList) {
		log.debug("computeAmount() entry");
		if(r==null || learningJourneyList==null || learningJourneyList.isEmpty())
			return r;
		for(LearningJourneyObj lj: learningJourneyList)
			if(r.getLearningJourney()==lj.getId().intValue())
				r.setAmount(lj.getCost());
		return r;
	}
	
//	protected boolean hasDiscount(RegisterObj r) {
//		return isTodayBeforeEarlyBird()
//				|| !StringUtils.isBlank(r.getPromoCode());
//	}
	
	
	public RegisterObj initRegisterObj(RegisterObj r) {
		if(r==null)
			return r;
		return r;
	}
	
	@SuppressWarnings("unchecked")
	@Transactional(readOnly=true)
	public List<RegisterObj> getAllRegisterObj(){
		log.debug("update() entry");
		Criteria c = sessionFactory.getCurrentSession().createCriteria(RegisterObj.class);
		c.addOrder(Order.asc("id"));
		c.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
		return c.list();
	}
	
	@Transactional(readOnly=true)
	public RegisterObj get(Long id) {
		return (RegisterObj) sessionFactory.getCurrentSession().get(RegisterObj.class, id);
	}
	
	public void update(RegisterObj r) {
		log.debug("update() entry");
		r.setUpdatedDate(Calendar.getInstance().getTime());
		Session ses = sessionFactory.getCurrentSession();
		ses.update(r);
		if(r.countParticipants()<=0)
			return;
		for(ParticipantObj p: r.getParticipantList())
			ses.update(p);
	}
	
	public void processRegistration(RegisterObj r) {
		saveRegistration(r);
	}
	
	public void processRegistrationFree(RegisterObj r) {
		confirmAllStatus(r);
		saveRegistration(r);
		emailDao.sendEmail(r);
	}
	
	public void saveRegistration(RegisterObj r) {
		log.debug("saveRegistration() entry");
		r.setCreatedDate(Calendar.getInstance().getTime());
		r.setCreatedDateForAll(r.getCreatedDate());
		sessionFactory.getCurrentSession().save(r);
		log.debug("saveRegistration() exit");
	}
	
	public void saveOrUpdate(RegisterObj r) {
		log.debug("saveOrUpdate() entry");
		r.setCreatedDate(Calendar.getInstance().getTime());
		sessionFactory.getCurrentSession().saveOrUpdate(r);
		log.debug("saveOrUpdate() exit");
	}
	
	public void confirmAllStatus(RegisterObj r) {
		log.debug("confirmAllStatus() entry");
		setAllStatus(r, 1);
	}
	
	public void setAllStatus(RegisterObj r, int status) {
		log.debug("setAllStatus() entry");
		r.setStatus(status);
		if(r.countParticipants()<=0)
			return;
		for(ParticipantObj p: r.getParticipantList())
			p.setStatus(status);
	}
	
	public void setAllStatusAndUpdate(List<RegisterObj> regList, RegisterFilter filter, int status) {
		for(RegisterObj r: regList)
			if(r.getId().equals(filter.getId())) {
				setAllStatus(r, status);
				update(r);
				return;
			}
	}
	
	public void addParticipant(RegisterObj r) {
		if(r==null)
			return;
		r.setI(r.getParticipantCount());
		r.getParticipantList().add(new ParticipantObj());
	}
	
	public void clearParticipants(RegisterObj r) {
		if(r==null)
			return;
		r.getParticipantList().clear();
		r.setI(0);
	}
	
	public void deleteLastParticipant(RegisterObj r) {
		if(r==null || r.getParticipantCount()<=0)
			return;
		r.getParticipantList().remove(r.getParticipantCount()-1);
	}
	
	@SuppressWarnings("unchecked")
	public RegisterCountObj getRegisterCountObjLj(List<LearningJourneyObj> learningJourneyList) {
		log.debug("getRegisterCountObjLj() entry");
		RegisterCountObj rc = buildRegisterCountObjLj(learningJourneyList);
		Criteria c = sessionFactory.getCurrentSession().createCriteria(RegisterObj.class);
		c.add(Restrictions.eq("regType", 1));
		c.add(Restrictions.or(Restrictions.eq("status", 1), Restrictions.ge("createdDate", getCutOffDate())));
		c.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
		List<RegisterObj> regList = c.list();
		if(regList==null)
			return rc;
		for(RegisterObj r: regList) {
			if(rc.getLearningJourney().containsKey(r.getLearningJourney().longValue())) {
				rc.getLearningJourney().put(r.getLearningJourney().longValue(), rc.getLearningJourney().get(r.getLearningJourney().longValue())+1);
			}
		}
		log.debug("getRegisterCountObjLj() returning:"+rc);
		return rc;
	}
	
	protected Date getCutOffDate() {
		Calendar cutoffcal = Calendar.getInstance();
		cutoffcal.add(Calendar.MINUTE, -RESERVE_SEAT_IN_MINUTES);
		return cutoffcal.getTime();
	}
	
	@SuppressWarnings("unchecked")
	public RegisterCountObj getRegisterCountObj(List<SessionObj> sessionList) {
		RegisterCountObj rc = buildRegisterCountObj(sessionList);
		Session ses = sessionFactory.getCurrentSession();
		Criteria c = ses.createCriteria(RegisterObj.class);
		c.add(Restrictions.or(Restrictions.eq("status", 1), Restrictions.ge("createdDate", getCutOffDate())));
		c.add(Restrictions.eq("regType", 0));
		c.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
		List<RegisterObj> regList = c.list();
		if(regList==null)
			return rc;
		for(RegisterObj r: regList) {
			if(rc.getSessionA().containsKey(r.getSessionA()))
				rc.getSessionA().put(r.getSessionA(), rc.getSessionA().get(r.getSessionA())+1);
			if(rc.getSessionB().containsKey(r.getSessionB()))
				rc.getSessionB().put(r.getSessionB(), rc.getSessionB().get(r.getSessionB())+1);
			if(rc.getSessionC().containsKey(r.getSessionC()))
				rc.getSessionC().put(r.getSessionC(), rc.getSessionC().get(r.getSessionC())+1);
			if(rc.getSessionD().containsKey(r.getSessionD()))
				rc.getSessionD().put(r.getSessionD(), rc.getSessionD().get(r.getSessionD())+1);
			if(r.getParticipantCount()<=0)
				continue;
			for(ParticipantObj p: r.getParticipantList()) {
				if(rc.getSessionA().containsKey(p.getSessionA()))
					rc.getSessionA().put(p.getSessionA(), rc.getSessionA().get(p.getSessionA())+1);
				if(rc.getSessionB().containsKey(p.getSessionB()))
					rc.getSessionB().put(p.getSessionB(), rc.getSessionB().get(p.getSessionB())+1);
				if(rc.getSessionC().containsKey(p.getSessionC()))
					rc.getSessionC().put(p.getSessionC(), rc.getSessionC().get(p.getSessionC())+1);
				if(rc.getSessionD().containsKey(p.getSessionD()))
					rc.getSessionD().put(p.getSessionD(), rc.getSessionD().get(p.getSessionD())+1);
			}
		}
		return rc;
	}
	
	protected RegisterCountObj buildRegisterCountObjLj(List<LearningJourneyObj> learningJourneyList) {
		RegisterCountObj rc = new RegisterCountObj();
		if(learningJourneyList==null||learningJourneyList.isEmpty())
			return rc;
		for(LearningJourneyObj s: learningJourneyList) {
			rc.getLearningJourney().put(s.getId(), 0);
		}
		return rc;
	}
	
	protected RegisterCountObj buildRegisterCountObj(List<SessionObj> sessionList) {
		RegisterCountObj rc = new RegisterCountObj();
		if(sessionList==null||sessionList.isEmpty())
			return rc;
		for(SessionObj s: sessionList) {
			switch(s.getType()) {
			case 1:
				rc.getSessionA().put(s.getNo(), 0);
				break;
			case 2:
				rc.getSessionB().put(s.getNo(), 0);
				break;
			case 3:
				rc.getSessionC().put(s.getNo(), 0);
				break;
			case 4:
				rc.getSessionD().put(s.getNo(), 0);
				break;
			}
		}
		return rc;
	}
}
